﻿using System;
using System.Collections.Concurrent;
using System.Threading;

namespace EasyConveyor
{
	/// <summary>
	/// An abstract class encapsulating pipeline activation functionality. All activators should derive from this class.
	/// </summary>
	public abstract class ActivatorBase : ThreadSafeDisposePatternBase
	{
		private readonly ManualResetEvent _activationEvent = new ManualResetEvent(false);
		private readonly ConcurrentQueue<object> _activationRequests = new ConcurrentQueue<object>();
		private readonly object _getNextLocker = new object();
		private readonly object _activateLocker = new object();
		internal bool IsAttachedToPipeline { private get; set; }
		
		internal object GetNext()
		{
			CheckDisposed();

			lock (_getNextLocker)
			{
				if (_activationRequests.Count == 0)
					if (!_activationEvent.WaitOne(2000))
						return null;

				_activationEvent.Reset();

				object toReturn;
				_activationRequests.TryDequeue(out toReturn);
				return toReturn;
			}
		}

		/// <summary>
		/// This method is called in a derived class to start a pipeline worker.
		/// </summary>
		/// <param name="context">An object representing the context that is transferred from one middleware instance to the next in the pipeline. May not be null.</param>
		/// <exception cref="ArgumentNullException">Thrown when the value passed for <paramref name="context"/> is null.</exception>
		protected void Activate(object context)
		{
			CheckDisposed();

			lock (_activateLocker)
			{
				if (!IsAttachedToPipeline)
					return;

				if (context == null)
					throw new ArgumentNullException("context");

				_activationRequests.Enqueue(context);
				_activationEvent.Set();
			}
		}

		/// <summary>
		/// When overridden in a derived class, disposes of unmanaged resources.
		/// IDisposable.Dispose should not be implemented explicitly by the derived class when this method is overridden.
		/// </summary>
		/// <example>
		/// <code>
		/// protected override void DisposeUnmanagedResources()
		/// {
		///		base.DisposeUnmanagedResources();
		///		_myUnmanagedResource.Dispose(); 
		/// }
		/// </code>
		/// </example>
		protected override void DisposeUnmanagedResources()
		{
			_activationEvent.Dispose();
		}
	}
}
